using System;
using System.Web;
using System.Collections;
using System.Xml;
using System.Reflection;
using Deepcode.Flux.Core.Services.XML;
using Deepcode.Flux.Core.Extensions.Codons;
using System.Collections.Specialized;

namespace Deepcode.Flux.Core.Extensions
{
	/// <summary>
	/// CodonFactory is responsible for understanding which codon object should be built
	/// in response to which xml tag in the addin definition. Asking it to create
	/// the codon, it will lookup the codon builder associated with the tag name and
	/// return the codon it builds.
	/// </summary>
	public class CodonFactory
	{
		// Scanned codons are stored in this hashtable, keyed by tagname
		public Hashtable codonHashtable = new Hashtable();

		/// <summary>
		/// This method reads the /Settings/Codons.xml.config file
		/// and scans the namespaces specified for ICodon derived objects.
		/// Each codon found is added to the factory with an associated
		/// builder.
		/// </summary>
		public void ImportNamespaces()
		{
			Deepcode.Flux.Core.Services.FileSystem.FileScanService search = new Deepcode.Flux.Core.Services.FileSystem.FileScanService();
			StringCollection dlls = search.SearchFilesInWebPath("~/bin/", "*.dll", false);
			foreach( string dllfile in dlls )
				ImportCodons(dllfile);
		}
		/// <summary>
		/// Discovers all codons within the specified DLL file and adds codon builders for them
		/// </summary>
		/// <param name="DLLFile"></param>
		private void ImportCodons( string DLLFile )
		{
			Assembly A = Assembly.LoadFrom( DLLFile );
			if( A == null ) throw new Exception("Couldn't load assembly " + DLLFile + " to scan for codons");

			System.Type[] types = A.GetTypes();
			foreach (System.Type t in types)
			{
				// Ensure type is not abstract and is descendent of abstract codon
				if ((!t.IsAbstract) && (t.IsSubclassOf(typeof(AbstractCodon))))
				{
					CodonBuilder cb = new CodonBuilder(t.AssemblyQualifiedName);
					this.InsertCodonBuilder(cb);
				}
			}
		}
		/// <summary>
		/// Inserts a codon builder into the hashtable
		/// </summary>
		/// <param name="builder"></param>
		private void InsertCodonBuilder( CodonBuilder builder )
		{
			codonHashtable[ builder.TagName ] = builder;
		}
		
		/// <summary>
		/// Creates a codon based on the specified tagnode
		/// </summary>
		/// <param name="tagnode"></param>
		/// <returns></returns>
		public ICodon CreateCodon( XmlNode tagnode )
		{
			ICodon result = null;
			CodonBuilder builder = codonHashtable[ tagnode.Name ] as CodonBuilder;
			if( builder != null )
			{
				result = builder.BuildCodon();
				if( result != null ) return result;
				throw new Exception("Tag " + tagnode.Name + " has an associated codon, but BuildCodon() failed on it");
			}
			throw new Exception("Tag " + tagnode.Name + " does not have an associated codon");
		}
	}
}
